package com.qiuling.iot.shiro;

import com.alibaba.fastjson.JSON;
import com.qiuling.iot.Exception.BusinessException;
import com.qiuling.iot.Exception.code.BaseResponseCode;
import com.qiuling.iot.constants.Constant;
import com.qiuling.iot.utils.DataResult;
import com.qiuling.iot.utils.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.*;

/**
 * @Author：zhangqiang
 * @Description：
 * @Date：Created in 1:47 2020/2/10
 * @Modified By：
 */
@Slf4j
public class CustomAccessControlerFilter extends AccessControlFilter {

    public static List<Map<String, Deque<Serializable>>> list = new ArrayList<>();

    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest,
                                      ServletResponse servletResponse, Object o) throws Exception {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest,
                                     ServletResponse servletResponse) throws Exception {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        log.info(request.getMethod());
        log.info(request.getRequestURL().toString());

        //判断客户端是否携带accessToken
        try {
            String accessToken = request.getHeader(Constant.ACCESS_TOKEN);
            if (StringUtils.isEmpty(accessToken)) {
                throw new BusinessException(BaseResponseCode.TOKEN_NOT_NULL);
            }


            /**
             * 异地登录提醒
             * 该段代码会影响性能，待以后优化
             */
            Subject subject = getSubject(request, servletResponse);
            Session session = subject.getSession();
            Serializable sessionId = session.getId();
            String username = JwtTokenUtil.getUsername(accessToken);
            // 读取缓存用户 没有就存入
            if (list.size() > 0) {
                for(int i=0; i<list.size(); i++) {
                    Map<String, Deque<Serializable>> map = list.get(i);

                    if (!map.isEmpty()) {
                        if (map.containsKey(username)) {
                            Deque<Serializable> deque = map.get(username);
                            if (deque == null) {
                                // 初始化队列
                                deque = new ArrayDeque<>();
                            }
                            // 如果队列里没有此sessionId，且用户没有被踢出；放入队列
                            if (!deque.contains(sessionId)
                                    && map.containsKey(username)) {
                                /**
                                 * 异地登录
                                 */
                                deque.push(sessionId);
                                deque.removeLast();
                                // 将用户的sessionId队列缓存
                                map.put(username, deque);
                                customRsponse(BaseResponseCode.ACCOUNT_IS_LOGIN.getCode(),
                                        BaseResponseCode.ACCOUNT_IS_LOGIN.getMsg(), servletResponse);
                                return false;
                            }
                        } else {
                            Deque<Serializable> deque = new ArrayDeque<>();
                            deque.push(sessionId);
                            map.put(username, deque);
                        }
                    } else {

                        Deque<Serializable> deque = new ArrayDeque<>();
                        deque.push(sessionId);
                        map.put(username, deque);
                        list.add(map);
                    }
                }
            } else {
                Map<String, Deque<Serializable>> map = new HashMap<>();
                Deque<Serializable> deque = new ArrayDeque<>();
                deque.push(sessionId);
                map.put(username, deque);
                list.add(map);
            }

            /**
             * 授权token，如果异地登录不通过，则不预授权
             */
            CustomUsernamePasswordToken customUsernamePasswordToken = new CustomUsernamePasswordToken(accessToken);
            getSubject(servletRequest, servletResponse).login(customUsernamePasswordToken);

        } catch (BusinessException e) {
            customRsponse(e.getCode(), e.getDefaultMessage(), servletResponse);
            return false;
        } catch (AuthenticationException e) {
            if (e.getCause() instanceof BusinessException) {
                BusinessException exception = (BusinessException) e.getCause();
                customRsponse(exception.getCode(), exception.getDefaultMessage(), servletResponse);
            } else {
                customRsponse(BaseResponseCode.SHIRO_AUTHENTICATION_ERROR.getCode(),
                        BaseResponseCode.SHIRO_AUTHENTICATION_ERROR.getMsg(), servletResponse);
            }
            return false;
        }
        return true;
    }

    /**
     * @param code
     * @param msg
     * @param response
     * @return void
     * @description 自定义错误响应
     * </br>
     * @author zhangqiang
     * @date 1:50 2020/2/10
     */
    private void customRsponse(int code, String msg, ServletResponse response) {
        // 自定义异常的类，用户返回给客户端相应的JSON格式的信息
        try {
            DataResult result = DataResult.getResult(code, msg);
            response.setContentType("application/json; charset=utf-8");
            response.setCharacterEncoding("UTF-8");
            String userJson = JSON.toJSONString(result);
            OutputStream out = response.getOutputStream();
            out.write(userJson.getBytes("UTF-8"));
            out.flush();
        } catch (IOException e) {
            log.error("eror={}", e);
        }
    }

}
